package cl.regalloc.parse;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;

public class BasicBlock {
	private Label blockLabel;
	private List<Instruction> block = new LinkedList<Instruction>();
	private List<BasicBlock> prdcs = new LinkedList<BasicBlock>();
	private List<BasicBlock> succs = new LinkedList<BasicBlock>();

	private HashSet<Register> inLive = new HashSet<Register>();
	private HashSet<Register> outLive = new HashSet<Register>();

	public BasicBlock() {
		super();
		System.out.printf("========= creating new BasicBlock... =========%n");
	}

	public BasicBlock(List<Instruction> block) {
		this.block = block;
		System.out.printf("========= creating new BasicBlock... =========%n");
	}

	public BasicBlock(Label blockLabel, List<Instruction> block, List<BasicBlock> prdcs, List<BasicBlock> succs) {
		this.blockLabel = blockLabel;
		this.block = block;
		this.prdcs = prdcs;
		this.succs = succs;
	}

	public boolean insertInstruction(Instruction newInstruction) {
		return this.block.add(newInstruction);
	}

	public boolean isEmpty() {
		return (this.block.isEmpty() && (this.blockLabel == null));
	}

	public List<Instruction> getBlock() {
		return block;
	}

	public List<BasicBlock> getPrdcs() {
		return prdcs;
	}

	public void addPrdc(BasicBlock prdc) {
		if (!this.prdcs.contains(prdc))
			this.prdcs.add(prdc);
	}

	public void addAllPrdcs(List<BasicBlock> prdcs) {
		for (BasicBlock currentPrdc : prdcs) {
			if (!this.prdcs.contains(currentPrdc))
				this.prdcs.add(currentPrdc);
		}
	}

	public boolean removePrdc(BasicBlock currPrdc) {
		if (this.prdcs.contains(currPrdc)) {
			this.prdcs.remove(currPrdc);
			return true;
		} else
			return false;
	}

	public boolean clearAllPrdcs() {
		for (BasicBlock currBasicBlock : this.prdcs)
			currBasicBlock.removePrdc(this);
		this.prdcs = new LinkedList<BasicBlock>();
		return true;
	}

	public List<BasicBlock> getSuccs() {
		return succs;
	}

	public void addSucc(BasicBlock succ) {
		if (!this.succs.contains(succ))
			this.succs.add(succ);
	}

	public void addAllSuccs(List<BasicBlock> succs) {
		for (BasicBlock currentSucc : succs) {
			if (!this.succs.contains(currentSucc))
				this.succs.add(currentSucc);
		}
	}

	public boolean removeSucc(BasicBlock currSucc) {
		if (this.succs.contains(currSucc)) {
			this.succs.remove(currSucc);
			return true;
		} else
			return false;
	}

	public boolean clearAllSuccs() {
		for (BasicBlock currBasicBlock : this.succs)
			currBasicBlock.removeSucc(this);
		this.succs = new LinkedList<BasicBlock>();
		return true;
	}

	public void setInLive(HashSet<Register> inLive) {
		this.inLive = inLive;
	}

	public HashSet<Register> getInLive() {
		return inLive;
	}

	public void setOutLive(HashSet<Register> outLive) {
		this.outLive = outLive;
	}

	public boolean updateOutLive() {

		boolean rs = true;
		// rebuild out-live
		this.outLive = new HashSet<Register>();
		// out-live is the disjunction of all its successors's in-live's
		for (BasicBlock currentSucc : this.succs) {
			HashSet<Register> currentInLive = currentSucc.getInLive();
			for (Register currentReg : currentInLive)
				if (!this.outLive.contains(currentReg))
					rs = (rs && this.outLive.add(currentReg));
		}
		return rs;
	}

	public HashSet<Register> getOutLive() {
		return outLive;
	}

	public void setLabel(Label currentLabel) {
		this.blockLabel = currentLabel;
	}

	public Label getLabel() {
		return this.blockLabel;
	}

	public boolean setInLiveAndUpdatePrdcs(HashSet<Register> newInLive) {
		Boolean isModified = false;	// result shows if in-live has been modified
		if (!newInLive.equals(this.inLive)) {
			isModified = true;
			// sets the new in-live
			this.setInLive(newInLive);
			//	update predecessors of the the basic block
			for (BasicBlock currentPrdc : this.prdcs)
				currentPrdc.updateOutLive();
		}
		return isModified;
	}
}
